from struc import server,vm
import numpy as np
import time
def model1(N,SERVER_LIST,M,VMS_LIST,T,R,ORDERS):

    '''
        模型选择的策略：
        1、统计峰值时在虚拟服务器下堆叠，不区分单双节点
        2、初步挑选服务器时，对所有服务器种类挨个遍历，选择时只考虑一种服务器用来满足需求，并计算仅选择该服务器下的开销
    '''

    '''
        1、SERVER - 已购买的服务器，列表存储，index表示server序号
        2、VMS - 已搭载虚拟机，字典存储
    '''

    capacity={'cpu':0,'memory':0}
    max_capa={'cpu':0,'memory':0}
    SERVERS=[]
    VMS={}
    '''
        0 前期准备
        0.1 计算投资回报比
            投资回报比的计算公式为=F1score(归一化(cpu投资回报比),归一化(memory投资回报比))
    '''
    SERVERS_ROI={}
    servers_cpu=np.zeros([1,N])
    servers_memory=np.zeros([1,N])
    servers_keys=list(SERVER_LIST.keys())
    for i in range(N):
        servers_cpu[0,i]=SERVER_LIST[servers_keys[i]].cpu
        servers_memory[0,i]=SERVER_LIST[servers_keys[i]].memory
    #归一化
    servers_cpu=(servers_cpu-np.min(servers_cpu))/(np.max(servers_cpu)-np.min(servers_cpu))
    servers_memory=(servers_memory-np.min(servers_memory))/(np.max(servers_memory)-np.min(servers_memory))
    servers_roi=2*(servers_cpu*servers_memory)/(servers_cpu+servers_memory)
    for i in range(N):
        SERVERS_ROI[servers_keys[i]]=servers_roi[0,i]
    for i in range(T):
        time_start=time.time()
        SERVERS_TODAY = []
        VMS_TODAY = {}
        order = ORDERS[i]
        '''
            1 统计峰值与写入虚拟机
        '''
        for j in range(len(order)):
            if order[j][0]==1:
                VMS_TODAY[order[j][2]]=vm([order[j][1]])
                capacity['cpu']+=VMS_LIST[order[j][1]].cpu
                capacity['memory']+=VMS_LIST[order[j][1]].memory
            else:
                capacity['cpu'] -= VMS_LIST[VMS[order[j][1]].name].cpu
                capacity['memory'] -= VMS_LIST[VMS[order[j][1]].name].memory
            #更新峰值信息
            if max_capa['cpu']<capacity['cpu']:
                max_capa['cpu']=capacity['cpu']
            if max_capa['memory']<capacity['memory']:
                max_capa['memory']=capacity['memory']
        '''
            2 初步挑选服务器
        '''
        max_cost = 0
        server_index = {'main': -1, 'sub': -1}
        best_n = {'main': 0, 'sub': 0}
        for j in range(len(SERVERS)):
            max_capa['cpu']-=SERVER_LIST[SERVERS[j].name].cpu
            max_capa['memory']-=SERVER_LIST[SERVERS[j].name].memory
        if (max_capa['cpu']>0)&(max_capa['memory']>0):
            '''
                2.1 挑选主要的服务器
            '''
            for key in SERVER_LIST.keys():
                n=max(max_capa['cpu']/SERVER_LIST[key].cpu,max_capa['memory']/SERVER_LIST[key].memory)
                cost=n*(SERVER_LIST[key].fixcost+T*SERVER_LIST[key].daycost)
                if max_cost<cost:
                    max_cost=cost
                    server_index['main']=key
                    best_n['main']=int(n)
            tmp_capa={'cpu':max(max_capa['cpu']-best_n['main']*SERVER_LIST[server_index['main']].cpu,0),'memory':max(max_capa['memory']-best_n['main']*SERVER_LIST[server_index['main']].memory,0)}
            max_cost=0
            '''
                2.2 挑选次要的服务器
            '''
            for key in SERVER_LIST.keys():
                n=int(max(tmp_capa['cpu']/SERVER_LIST[key].cpu,tmp_capa['memory']/SERVER_LIST[key].memory))+1
                cost=n*(SERVER_LIST[key].fixcost+T*SERVER_LIST[key].daycost)
                if max_cost<cost:
                    max_cost = cost
                    server_index['sub'] = key
                    best_n['sub'] = int(n)
            param={'cpu':SERVER_LIST[server_index['main']].cpu/2,'memory':SERVER_LIST[server_index['main']].memory/2}
            for j in range(best_n['main']):
                SERVERS_TODAY.append(server([server_index['main'],param,param]))
            param = {'cpu':SERVER_LIST[server_index['sub']].cpu / 2, 'memory':SERVER_LIST[server_index['sub']].memory / 2}
            for j in range(best_n['sub']):
                SERVERS_TODAY.append(server([server_index['sub'],param,param]))
        '''
            3 对虚拟机排序,本质上是重新排出一个key的顺序，并按照这一顺序依序读取
        '''
        VMS= {**VMS,**VMS_TODAY}
        vms_sort_keys=list(VMS_TODAY.keys())
        for j in range(len(vms_sort_keys)):
            for k in range(len(vms_sort_keys)-j-1):
                if VMS_LIST[VMS_TODAY[vms_sort_keys[k]].name].istwo<VMS_LIST[VMS_TODAY[vms_sort_keys[k+1]].name].istwo:
                    tmp_key=vms_sort_keys[k]
                    vms_sort_keys[k]=vms_sort_keys[k+1]
                    vms_sort_keys[k+1]=tmp_key
                elif VMS_LIST[VMS_TODAY[vms_sort_keys[k]].name].istwo>VMS_LIST[VMS_TODAY[vms_sort_keys[k+1]].name].istwo:
                    continue
                else:
                    cpu_rate = VMS_LIST[VMS_TODAY[vms_sort_keys[k]].name].cpu/VMS_LIST[VMS_TODAY[vms_sort_keys[k+1]].name].cpu
                    memory_rate=VMS_LIST[VMS_TODAY[vms_sort_keys[k]].name].memory/VMS_LIST[VMS_TODAY[vms_sort_keys[k+1]].name].memory
                    if (cpu_rate<=1)&(memory_rate<=1): #如果j完全落后于k
                        tmp_key = vms_sort_keys[k]
                        vms_sort_keys[k] = vms_sort_keys[k + 1]
                        vms_sort_keys[k + 1] = tmp_key
                    else: #如果j不完全落后于k
                        if (cpu_rate>1)&(memory_rate>1): #如果j完全不落后于k
                            continue
                        elif cpu_rate>1: #如果j的cpu完全不落后于k
                            if abs(cpu_rate-1)>abs(1/memory_rate-1): #如果j的cpu超出k的程度大于memory落后程度
                                continue
                            else:
                                tmp_key = vms_sort_keys[k]
                                vms_sort_keys[k] = vms_sort_keys[k + 1]
                                vms_sort_keys[k + 1] = tmp_key
                        else: #如果j的memory完全不落后于k
                            if abs(memory_rate-1)>abs(1/cpu_rate-1):#如果j的memory超出k的程度大于cpu落后程度
                                continue
                            else:
                                tmp_key = vms_sort_keys[k]
                                vms_sort_keys[k] = vms_sort_keys[k + 1]
                                vms_sort_keys[k + 1] = tmp_key
        '''
            4 Next-Fit映射虚拟机
            4.1 对服务器进行排序
        '''
        if (best_n['main']>0)&(best_n['sub']>0):
            SERVERS_TODAY_MAIN=SERVERS_TODAY[:best_n['main']]
            SERVERS_TODAY_SUB=SERVERS_TODAY[best_n['main']:len(SERVERS_TODAY)]
            if SERVER_LIST[SERVERS_TODAY_MAIN[0].name].daycost > SERVER_LIST[SERVERS_TODAY_SUB[0].name].daycost:
                SERVERS_TODAY=SERVERS_TODAY_SUB+SERVERS_TODAY_MAIN
            SERVERS=SERVERS+SERVERS_TODAY
        else:
            SERVERS = SERVERS + SERVERS_TODAY
        '''
            4.2 Next-Fit映射
        '''
        isfinish_orders=np.ones([1,len(order)])
        for j in range(len(vms_sort_keys)):
            VM=VMS_TODAY[vms_sort_keys[j]] #VM代表当前进行挑选的虚拟机
            # 映射前，首先完成删除操作
            index=0
            del_orders=[]
            flag=0
            for m in range(len(order)):
                if isfinish_orders[0,m]==0:
                    continue
                else:
                    if (order[m][0]==1):
                        if (order[m][2]==vms_sort_keys[j]):
                            isfinish_orders[0,m]=0
                            index=m
                            break
            for m in range(index):
                if isfinish_orders[0,m]==0:
                    continue
                else:
                    if order[m][0]==1:
                        flag=1
                        break
                    else:
                        del_orders.append(order[m])
            if flag==0:
                # 更新isfinish信息
                for m in range(index):
                    if order[m][0]==-1:
                        isfinish_orders[0,m]=0
                for m in range(len(del_orders)):
                    #更新服务器占用
                    if VMS[del_orders[m][1]].servernode==-1:
                        print(VMS[del_orders[m][1]].serverid)
                        print()
                    for char in VMS[del_orders[m][1]].servernode:
                        SERVERS[VMS[del_orders[m][1]].serverid].margin[char]['cpu']+=VMS_LIST[VMS[del_orders[m][1]].name].istwo\
                                                                                     *VMS_LIST[VMS[del_orders[m][1]].name].cpu+ \
                                                                                     (1-VMS_LIST[VMS[del_orders[m][1]].name].istwo)\
                                                                                     *(VMS_LIST[VMS[del_orders[m][1]].name].cpu/2)
                        SERVERS[VMS[del_orders[m][1]].serverid].margin[char]['memory'] += VMS_LIST[VMS[del_orders[m][1]].name].istwo \
                                                                                    * VMS_LIST[VMS[del_orders[m][1]].name].memory + \
                                                                                    (1 - VMS_LIST[VMS[del_orders[m][1]].name].istwo) \
                                                                                    * (VMS_LIST[VMS[del_orders[m][1]].name].memory / 2)
                    index=SERVERS[VMS[del_orders[m][1]].serverid].vmid.index(del_orders[m][1])
                    del SERVERS[VMS[del_orders[m][1]].serverid].vmid[index]
                    # for key in SERVERS[VMS[del_orders[m][1]].serverid].offset:
                    #     SERVERS[VMS[del_orders[m][1]].serverid].offset[key]=(1-SERVERS[VMS[del_orders[m][1]].serverid].margin[key]['cpu']/
                    #                                                         SERVER_LIST[SERVERS[VMS[del_orders[m][1]].serverid].name].cpu/2)/\
                    #                                                         (1 - SERVERS[VMS[del_orders[m][1]].serverid].margin[key]['memory'] /
                    #                                                         SERVER_LIST[SERVERS[VMS[del_orders[m][1]].serverid].name].memory / 2)
                    #删除虚拟机
                    del VMS[del_orders[m][1]]

            k=0

            while k!=len(SERVERS): #挨个寻找可以插入的服务器
                if VMS_LIST[VM.name].istwo==1: #双节点放置
                    min_cpu=min(SERVERS[k].margin['A']['cpu'],SERVERS[k].margin['B']['cpu'])
                    min_memory=min(SERVERS[k].margin['A']['memory'],SERVERS[k].margin['B']['memory'])
                    if (min_cpu>=(VMS_LIST[VM.name].cpu/2))&(min_memory>=(VMS_LIST[VM.name].memory/2)):
                        # 更新虚拟机信息
                        VM.serverid=k
                        VM.servernode='AB'
                        VM.offset=(VMS_LIST[VM.name].cpu/SERVER_LIST[SERVERS[k].name].cpu)/(VMS_LIST[VM.name].memory/SERVER_LIST[SERVERS[k].name].memory)
                        # 更新服务器信息
                        SERVERS[k].vmid.append(vms_sort_keys[j])
                        SERVERS[k].margin['A']['cpu'] -= VMS_LIST[VM.name].cpu / 2
                        SERVERS[k].margin['A']['memory'] -= VMS_LIST[VM.name].memory / 2
                        SERVERS[k].margin['B']['cpu'] -= VMS_LIST[VM.name].cpu / 2
                        SERVERS[k].margin['B']['memory'] -= VMS_LIST[VM.name].memory / 2
                    else:
                        if k==(len(SERVERS)-1): #如果找到最后仍没有服务器可以包容虚拟机
                            '''
                                开启新的服务器
                                以投资回报比衡量开启谁
                            '''
                            index=np.argsort(servers_roi)[::-1] #降序排列
                            for n in range(index.shape[1]):
                                if (SERVER_LIST[servers_keys[index[0,n]]].cpu>=VMS_LIST[VM.name].cpu)&(SERVER_LIST[servers_keys[index[0,n]]].memory>=VMS_LIST[VM.name].memory):
                                    #满足插入条件，生成服务器并插入
                                    param = {'cpu': SERVER_LIST[servers_keys[index[0,n]]].cpu / 2,
                                             'memory': SERVER_LIST[servers_keys[index[0,n]]].memory / 2}
                                    SERVERS.append(server([servers_keys[index[0,n]],param,param]))
                                    # 更新虚拟机信息
                                    k=k+1
                                    VM.serverid = k
                                    VM.servernode = 'AB'
                                    VM.offset = (VMS_LIST[VM.name].cpu / SERVER_LIST[SERVERS[k].name].cpu) / (
                                                VMS_LIST[VM.name].memory / SERVER_LIST[SERVERS[k].name].memory)
                                    # 更新服务器信息
                                    SERVERS[k].vmid.append(vms_sort_keys[j])
                                    SERVERS[k].margin['A']['cpu'] -= VMS_LIST[VM.name].cpu / 2
                                    SERVERS[k].margin['A']['memory'] -= VMS_LIST[VM.name].memory / 2
                                    SERVERS[k].margin['B']['cpu'] -= VMS_LIST[VM.name].cpu / 2
                                    SERVERS[k].margin['B']['memory'] -= VMS_LIST[VM.name].memory / 2
                else: #单节点放置
                    local=''
                    if (SERVERS[k].margin['A']['cpu']>=VMS_LIST[VM.name].cpu/2)&(SERVERS[k].margin['A']['memory']>=VMS_LIST[VM.name].memory/2):
                        local+='A'
                    if (SERVERS[k].margin['B']['cpu'] >= VMS_LIST[VM.name].cpu / 2) & (
                            SERVERS[k].margin['B']['memory'] >= VMS_LIST[VM.name].memory / 2):
                        local+='B'
                    if (local=='A')|(local=='B'):
                        # 更新虚拟机信息
                        VM.serverid = k
                        VM.servernode = local
                        VM.offset = (VMS_LIST[VM.name].cpu / (SERVER_LIST[SERVERS[k].name].cpu/2)) / (
                                    VMS_LIST[VM.name].memory / (SERVER_LIST[SERVERS[k].name].memory/2))
                        # 更新服务器信息
                        SERVERS[k].vmid.append(vms_sort_keys[j])
                        SERVERS[k].margin[local]['cpu'] -= VMS_LIST[VM.name].cpu
                        SERVERS[k].margin[local]['memory'] -= VMS_LIST[VM.name].memory
                    elif local=='AB':
                        # 挑选节点
                        cpu_rate=SERVERS[k].margin['A']['cpu']/SERVERS[k].margin['B']['cpu']
                        memory_rate=SERVERS[k].margin['A']['memory']/SERVERS[k].margin['B']['memory']
                        if (cpu_rate>=1)&(memory_rate>=1):
                            local='A'
                        elif (cpu_rate<1)&(memory_rate<1):
                            local='B'
                        else:
                            choice=np.random.random_sample()
                            if choice<0.5:
                                local='A'
                            else:
                                local='B'
                        # 更新虚拟机信息
                        VM.serverid = k
                        VM.servernode = local
                        VM.offset = (VMS_LIST[VM.name].cpu / (SERVER_LIST[SERVERS[k].name].cpu / 2)) / (
                                VMS_LIST[VM.name].memory / (SERVER_LIST[SERVERS[k].name].memory / 2))
                        # 更新服务器信息
                        SERVERS[k].vmid.append(vms_sort_keys[j])
                        SERVERS[k].margin[local]['cpu'] -= VMS_LIST[VM.name].cpu
                        SERVERS[k].margin[local]['memory'] -= VMS_LIST[VM.name].memory
                    else:
                        if k==len(SERVERS)-1: #如果找到最后仍没有服务器可以包容虚拟机
                            '''
                                开启新的服务器
                                以投资回报比衡量开启谁
                            '''
                            index=np.argsort(servers_roi)[::-1] #降序排列
                            for n in range(index.shape[1]):
                                if ((SERVER_LIST[servers_keys[index[0,n]]].cpu/2)>=VMS_LIST[VM.name].cpu)&((SERVER_LIST[servers_keys[index[0,n]]].memory/2)>=VMS_LIST[VM.name].memory):
                                    #满足插入条件，生成服务器并插入
                                    param = {'cpu': SERVER_LIST[servers_keys[index[0,n]]].cpu / 2,
                                             'memory': SERVER_LIST[servers_keys[index[0,n]]].memory / 2}
                                    SERVERS.append(server([servers_keys[index[0,n]],param,param]))
                                    # 更新虚拟机信息
                                    k=k+1
                                    VM.serverid = k
                                    choice=np.random.sample()
                                    if choice<0.5:
                                        VM.servernode = 'A'
                                    else:
                                        VM.servernode = 'B'
                                    VM.offset = (VMS_LIST[VM.name].cpu / (SERVER_LIST[SERVERS[k].name].cpu / 2)) / (
                                            VMS_LIST[VM.name].memory / (SERVER_LIST[SERVERS[k].name].memory / 2))
                                    # 更新服务器信息
                                    SERVERS[k].vmid.append(vms_sort_keys[j])
                                    SERVERS[k].margin[VM.servernode]['cpu'] -= VMS_LIST[VM.name].cpu
                                    SERVERS[k].margin[VM.servernode]['memory'] -= VMS_LIST[VM.name].memory
                k+=1

            if VMS[vms_sort_keys[j]].serverid == -1: #监视过程，检验是否有虚拟机没有完成分配
                print('错误，有虚拟机没有被分配！')
        time_end=time.time()
        print('day',i,'运行时间',time_end-time_start)